package yyl.mvc.common.http;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.util.Locale;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import yyl.mvc.common.lang.AssertUtil;

/**
 * Internal static utilities for handling data.
 */
public final class DataUtil {

	static final String DEFAULT_CHARSET = "UTF-8"; // used if not found in header or meta charset
	private static final Pattern CHARSET_PATTERN = Pattern.compile("(?i)\\bcharset=\\s*(?:\"|')?([^\\s,;\"']*)");
	private static final int BOUNDARY_LENGTH = 32;
	private static final int BUFFER_SIZE = 0x20000; // ~130K.
	private static final char[] mimeBoundaryChars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

	private DataUtil() {
	}

	/**
	 * Read the input stream into a byte buffer.
	 * @param inStream the input stream to read from
	 * @param maxSize the maximum size in bytes to read from the stream. Set to 0 to be unlimited.
	 * @return the filled byte buffer
	 * @throws IOException if an exception occurs whilst reading from the input stream.
	 */
	static ByteBuffer readToByteBuffer(InputStream inStream, int maxSize) throws IOException {
		AssertUtil.isTrue(maxSize >= 0, "maxSize must be 0 (unlimited) or larger");
		final boolean capped = maxSize > 0;
		byte[] buffer = new byte[BUFFER_SIZE];
		ByteArrayOutputStream outStream = new ByteArrayOutputStream(BUFFER_SIZE);
		int read;
		int remaining = maxSize;

		while (true) {
			read = inStream.read(buffer);
			if (read == -1)
				break;
			if (capped) {
				if (read > remaining) {
					outStream.write(buffer, 0, remaining);
					break;
				}
				remaining -= read;
			}
			outStream.write(buffer, 0, read);
		}
		return ByteBuffer.wrap(outStream.toByteArray());
	}

	static ByteBuffer emptyByteBuffer() {
		return ByteBuffer.allocate(0);
	}

	/**
	 * Parse out a charset from a content type header. If the charset is not supported, returns null (so the default will kick in.)
	 * @param contentType e.g. "text/html; charset=UTF-8"
	 * @return "UTF-8", or null if not found. Charset is trimmed and uppercased.
	 */
	static String getCharsetFromContentType(String contentType) {
		if (contentType == null) {
			return null;
		}
		Matcher m = CHARSET_PATTERN.matcher(contentType);
		if (m.find()) {
			String charset = m.group(1).trim();
			charset = charset.replace("charset=", "");
			return validateCharset(charset);
		}
		return null;
	}

	private static String validateCharset(String cs) {
		if (cs == null || cs.length() == 0)
			return null;
		cs = cs.trim().replaceAll("[\"']", "");
		try {
			if (Charset.isSupported(cs))
				return cs;
			cs = cs.toUpperCase(Locale.ENGLISH);
			if (Charset.isSupported(cs))
				return cs;
		} catch (IllegalCharsetNameException e) {
			// if our this charset matching fails.... we just take the default
		}
		return null;
	}

	/**
	 * Creates a random string, suitable for use as a mime boundary
	 */
	static String mimeBoundary() {
		final StringBuilder mime = new StringBuilder(BOUNDARY_LENGTH);
		final Random rand = new Random();
		for (int i = 0; i < BOUNDARY_LENGTH; i++) {
			mime.append(mimeBoundaryChars[rand.nextInt(mimeBoundaryChars.length)]);
		}
		return mime.toString();
	}

	static String encodeUrl(String url) {
		if (url == null)
			return null;
		return url.replaceAll(" ", "%20");
	}

	static String encodeMimeName(String val) {
		if (val == null)
			return null;
		return val.replaceAll("\"", "%22");
	}
}
